package org.qpcr.community.standalone.web;

import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import org.hswebframework.ezorm.rdb.mapping.ReactiveQuery;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.qpcr.community.device.entity.DeviceInstanceEntity;
import org.qpcr.community.device.enums.DeviceState;
import org.qpcr.community.device.service.LocalDeviceInstanceService;
import org.qpcr.community.device.service.LocalDeviceProductService;
import org.qpcr.community.project.service.LocalExperimentalRecordsInstanceService;
import org.qpcr.community.rule.engine.service.AlarmRecordService;
import org.qpcr.community.standalone.web.request.AlarmConfigCountReq;
import org.qpcr.community.standalone.web.request.AlarmCountReq;
import org.qpcr.community.standalone.web.request.DeviceDashboardReq;
import org.qpcr.community.standalone.web.response.dashboard.*;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RequestMapping("/qpcr-dashboard")
@RestController
@AllArgsConstructor
public class QpcrDashboardController {
    @Resource
    private LocalDeviceInstanceService deviceInstanceService;
    @Resource
    private LocalDeviceProductService  deviceProductService;
    @Resource
    private LocalExperimentalRecordsInstanceService  experimentalRecordsInstanceService;

    @Resource
    private AlarmRecordService alarmRecordService;




    @PostMapping("/overview")
    @Operation(summary = "设备仪表盘")
    public Mono<DashboardData> dashboardOverview(@RequestBody DeviceDashboardReq req) {
        if (req.getExperimentDate() == null || req.getExperimentDate().trim().isEmpty()) {
            req.setExperimentDate("week");
        }

        return Mono.zip(
            // 产品统计
            getProductStats(),
            // 设备统计
            getDeviceStats(req.getDeviceIds()),
            // 今日实验统计
            getTodayStats(req.getDeviceIds()),
            // 实验趋势
            getExperimentTrend(req.getExperimentDate(),req.getDeviceIds())
        ).map(tuple -> new DashboardData(
            tuple.getT1(), // productStats
            tuple.getT2(), // deviceStats
            tuple.getT3(), // todayStats
            tuple.getT4()  // experimentTrend
        ));
    }


    @PostMapping("/alarm/level/count")
    @Operation(summary = "告警级别统计")
    public Mono<AlarmCountResp> alarmLevelCount(@RequestBody AlarmCountReq req) {
        // 1. 计算时间范围
        Map<String, LocalDateTime> dateRange = org.qpcr.community.standalone.web.response.dashboard.ExperimentDateEnums.getDateRange(req.getPeriodType());
        LocalDateTime startDate = dateRange.get("start");
        LocalDateTime endDate = dateRange.get("end");
        long startMillis = startDate.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
        long endMillis = endDate.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();

        // 2. 查询告警记录
        return alarmRecordService.createQuery()
                .between("alarmTime", startMillis, endMillis)
                .fetch()
                .collectList()
                .map(records -> {
                    // 3. 生成xData
                    List<String> xData = new ArrayList<>();
                    int count = 1;
                    String periodType = req.getPeriodType();
                    if ("week".equalsIgnoreCase(periodType)) {
                        count = 7;
                    } else if ("month".equalsIgnoreCase(periodType)) {
                        count = 30;
                    }
                    java.time.LocalDate today = java.time.LocalDate.now();
                    java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("MM-dd");
                    for (int i = count - 1; i >= 0; i--) {
                        xData.add(today.minusDays(i).format(formatter));
                    }

                    // 4. 按天和级别统计
                    List<Integer> allLevels = records.stream().map(r -> r.getLevel()).distinct().sorted().collect(java.util.stream.Collectors.toList());
                    List<LevelSeries> seriesList = new ArrayList<>();
                    for (Integer level : allLevels) {
                        LevelSeries levelSeries = new LevelSeries();
                        levelSeries.setName("级别" + level);
                        // 匹配图标颜色
                        String color;
                        switch (level != null ? level : 0) {
                            case 1: color = "#F5222D"; break;
                            case 2: color = "#FA8C16"; break;
                            case 3: color = "#FAAD14"; break;
                            case 4: color = "#BFBFBF"; break;
                            case 5: color = "#8C8C8C"; break;
                            default: color = "#d9d9d9"; break;
                        }
                        levelSeries.setColor(color);
                        List<Integer> data = new ArrayList<>();
                        for (int i = count - 1; i >= 0; i--) {
                            java.time.LocalDate day = today.minusDays(i);
                            long start = day.atStartOfDay(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
                            long end = day.plusDays(1).atStartOfDay(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
                            int value = (int) records.stream()
                                    .filter(r -> r.getLevel() != null && r.getLevel().equals(level))
                                    .filter(r -> r.getAlarmTime() != null && r.getAlarmTime() >= start && r.getAlarmTime() < end)
                                    .count();
                            data.add(value);
                        }
                        levelSeries.setData(data);
                        seriesList.add(levelSeries);
                    }
                    AlarmCountResp resp = new AlarmCountResp();
                    resp.setXData(xData);
                    resp.setSeries(seriesList);
                    return resp;
                });
    }


    @PostMapping("/alarm/config/count")
    @Operation(summary = "告警配置统计")
    public Mono<AlarmConfigCountResp> alarmConfigCount(@RequestBody AlarmConfigCountReq req) {
        // 1. 计算时间范围
        Map<String, LocalDateTime> dateRange = org.qpcr.community.standalone.web.response.dashboard.ExperimentDateEnums.getDateRange(req.getPeriodType());
        LocalDateTime startDate = dateRange.get("start");
        LocalDateTime endDate = dateRange.get("end");
        long startMillis = startDate.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
        long endMillis = endDate.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();

        // 2. 查询告警记录
        return alarmRecordService.createQuery()
                .between("alarmTime", startMillis, endMillis)
                .fetch()
                .collectList()
                .map(records -> {
                    // 3. 生成xdata
                    List<String> xdata = new ArrayList<>();
                    int days = 1;
                    String periodType = req.getPeriodType();
                    if ("week".equalsIgnoreCase(periodType)) {
                        days = 7;
                    } else if ("month".equalsIgnoreCase(periodType)) {
                        days = 30;
                    }
                    java.time.LocalDate today = java.time.LocalDate.now();
                    java.time.format.DateTimeFormatter formatter = java.time.format.DateTimeFormatter.ofPattern("MM-dd");
                    for (int i = days - 1; i >= 0; i--) {
                        xdata.add(today.minusDays(i).format(formatter));
                    }

                    // 4. 按天和配置名称统计
                    List<String> allConfigs = records.stream().map(r -> r.getAlarmName()).distinct().collect(java.util.stream.Collectors.toList());
                    List<String> colorList = java.util.Arrays.asList(
                        "#2066F9", "#FF7B8A", "#52C41A", "#FAAD14", "#13C2C2", "#B37FEB", "#F759AB", "#A0D911", "#FA8C16", "#8C8C8C"
                    );
                    java.util.Random random = new java.util.Random();
                    List<ConfigSeries> seriesList = new ArrayList<>();
                    int colorIdx = 0;
                    for (String config : allConfigs) {
                        ConfigSeries configSeries = new ConfigSeries();
                        configSeries.setName(config);
                        // 随机分配颜色
                        String color = colorList.get(colorIdx % colorList.size());
                        colorIdx++;
                        configSeries.setColor(color);
                        List<Integer> data = new ArrayList<>();
                        for (int i = days - 1; i >= 0; i--) {
                            java.time.LocalDate day = today.minusDays(i);
                            long start = day.atStartOfDay(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
                            long end = day.plusDays(1).atStartOfDay(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
                            int value = (int) records.stream()
                                    .filter(r -> r.getAlarmName() != null && r.getAlarmName().equals(config))
                                    .filter(r -> r.getAlarmTime() != null && r.getAlarmTime() >= start && r.getAlarmTime() < end)
                                    .count();
                            data.add(value);
                        }
                        configSeries.setData(data);
                        seriesList.add(configSeries);
                    }
                    AlarmConfigCountResp resp = new AlarmConfigCountResp();
                    resp.setXdata(xdata);
                    resp.setSeries(seriesList);
                    return resp;
                });
    }

















    /*******************************************************************************************************************/
    /*******************************************************Private方法**************************************************/
    /*******************************************************************************************************************/






    /**
     * 获取产品统计数据
     */
    private Mono<List<StatItem>> getProductStats() {
        return deviceProductService.query(new QueryParamEntity())
            .collectList()
            .map(products -> {
                long totalProducts = products.size();
                long normalProducts = products.stream()
                    .filter(product -> product.getState() != null && product.getState() == 1)
                    .count();
                long disabledProducts = totalProducts - normalProducts;

                List<StatItem> productStats = new ArrayList<>();
                productStats.add(new StatItem("正常", (int) normalProducts, "#52c41a"));
                productStats.add(new StatItem("禁用", (int) disabledProducts, "#d9d9d9"));
                return productStats;
            });
    }

    /**
     * 获取设备统计数据
     */
    private Mono<List<StatItem>> getDeviceStats(List<String> deviceIds) {
        return deviceInstanceService.createQuery().in("id",deviceIds).fetch()
            .collectList()
            .map(devices -> {
                long totalDevices = devices.size();
                long onlineDevices = devices.stream()
                    .filter(device -> DeviceState.online.equals(device.getState()))
                    .count();
                long offlineDevices = devices.stream()
                    .filter(device -> DeviceState.offline.equals(device.getState()))
                    .count();
                long faultDevices = devices.stream()
                    .filter(device -> DeviceState.fault.equals(device.getState()))
                    .count();
                long runningDevices = devices.stream()
                    .filter(device -> DeviceState.running.equals(device.getState()))
                    .count();

                List<StatItem> deviceStats = new ArrayList<>();
                deviceStats.add(new StatItem("在线", (int) onlineDevices, "#52c41a"));
                deviceStats.add(new StatItem("离线", (int) offlineDevices, "#d9d9d9"));
                deviceStats.add(new StatItem("故障", (int) faultDevices, "#f5222d"));
                deviceStats.add(new StatItem("实验中", (int) runningDevices, "#1890ff"));
                return deviceStats;
            });
    }

    /**
     * 获取今日实验统计数据
     */
    private Mono<List<StatItem>> getTodayStats(List<String> deviceIds) {
        LocalDateTime todayStart = LocalDateTime.now().toLocalDate().atStartOfDay();
        LocalDateTime todayEnd = todayStart.plusDays(1).minusNanos(1);
        long startMillis = todayStart.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
        long endMillis = todayEnd.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
        return experimentalRecordsInstanceService.createQuery().between("receiveTime", startMillis, endMillis).in("deviceId",deviceIds).fetch()
                                                         .collectList()
            .map(records -> {
                long totalTodayExperiments = records.size();
                long completedExperiments = records.stream()
                    .filter(record -> "100".equals(record.getState()))
                    .count();
                long unfinishedExperiments = records.stream()
                    .filter(record -> record.getState() != null && !"100".equals(record.getState()))
                    .count();
                // 异常数据 = 总数据 - 完成数据 - 未完成数据
                long abnormalExperiments = totalTodayExperiments - completedExperiments - unfinishedExperiments;

                List<StatItem> todayStats = new ArrayList<>();
                todayStats.add(new StatItem("完成", (int) completedExperiments, "#52c41a"));
                todayStats.add(new StatItem("未完成", (int) unfinishedExperiments, "#d9d9d9"));
                // 只有当异常数据大于0时才添加异常项
                if (abnormalExperiments > 0) {
                    todayStats.add(new StatItem("异常", (int) abnormalExperiments, "#f5222d"));
                }
                return todayStats;
            });
    }

    /**
     * 获取实验趋势数据
     */
    private Mono<List<ExperimentTrend>> getExperimentTrend(String experimentDate, List<String> deviceIds) {
        Map<String, LocalDateTime> dateRange = ExperimentDateEnums.getDateRange(experimentDate);
        LocalDateTime startDate = dateRange.get("start");
        LocalDateTime endDate = dateRange.get("end");
        long startMillis = startDate.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
        long endMillis = endDate.atZone(java.time.ZoneId.systemDefault()).toInstant().toEpochMilli();
        return experimentalRecordsInstanceService.createQuery().between("receiveTime", startMillis, endMillis).in("deviceId",deviceIds).fetch()
            .collectList()
            .map(records -> {
                List<ExperimentTrend> experimentTrend = new ArrayList<>();

                switch (experimentDate) {
                    case "today":
                        // 今日：生成24小时的数据点
                        for (int i = 0; i < 24; i++) {
                            LocalDateTime currentTime = startDate.plusHours(i);
                            String date = currentTime.format(DateTimeFormatter.ofPattern("HH:mm"));

                            LocalDateTime hourStart = currentTime;
                            LocalDateTime hourEnd = currentTime.plusHours(1);

                            long completed = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(hourStart) || recordDateTime.isEqual(hourStart)) &&
                                           recordDateTime.isBefore(hourEnd) &&
                                           "100".equals(record.getState());
                                })
                                .count();

                            long abnormal = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(hourStart) || recordDateTime.isEqual(hourStart)) &&
                                           recordDateTime.isBefore(hourEnd) &&
                                           record.getState() != null && !"100".equals(record.getState());
                                })
                                .count();

                            experimentTrend.add(new ExperimentTrend(date, (int) completed, (int) abnormal));
                        }
                        break;

                    case "week":
                        // 近一周：生成7天的数据点
                        for (int i = 0; i < 7; i++) {
                            LocalDateTime currentDate = startDate.plusDays(i);
                            String date = currentDate.format(DateTimeFormatter.ofPattern("MM-dd"));

                            LocalDateTime dayStart = currentDate.toLocalDate().atStartOfDay();
                            LocalDateTime dayEnd = dayStart.plusDays(1).minusNanos(1);

                            long completed = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(dayStart) || recordDateTime.isEqual(dayStart)) &&
                                           recordDateTime.isBefore(dayEnd) &&
                                           "100".equals(record.getState());
                                })
                                .count();

                            long abnormal = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(dayStart) || recordDateTime.isEqual(dayStart)) &&
                                           recordDateTime.isBefore(dayEnd) &&
                                           record.getState() != null && !"100".equals(record.getState());
                                })
                                .count();

                            experimentTrend.add(new ExperimentTrend(date, (int) completed, (int) abnormal));
                        }
                        break;

                    case "month":
                        // 近一个月：生成30天的数据点
                        for (int i = 0; i < 30; i++) {
                            LocalDateTime currentDate = startDate.plusDays(i);
                            String date = currentDate.format(DateTimeFormatter.ofPattern("MM-dd"));

                            LocalDateTime dayStart = currentDate.toLocalDate().atStartOfDay();
                            LocalDateTime dayEnd = dayStart.plusDays(1).minusNanos(1);

                            long completed = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(dayStart) || recordDateTime.isEqual(dayStart)) &&
                                           recordDateTime.isBefore(dayEnd) &&
                                           "100".equals(record.getState());
                                })
                                .count();

                            long abnormal = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(dayStart) || recordDateTime.isEqual(dayStart)) &&
                                           recordDateTime.isBefore(dayEnd) &&
                                           record.getState() != null && !"100".equals(record.getState());
                                })
                                .count();

                            experimentTrend.add(new ExperimentTrend(date, (int) completed, (int) abnormal));
                        }
                        break;

                    default:
                        // 默认生成近一周的数据，使用startDate而不是当前时间
                        for (int i = 0; i < 7; i++) {
                            LocalDateTime currentDate = startDate.plusDays(i);
                            String date = currentDate.format(DateTimeFormatter.ofPattern("MM-dd"));

                            LocalDateTime dayStart = currentDate.toLocalDate().atStartOfDay();
                            LocalDateTime dayEnd = dayStart.plusDays(1).minusNanos(1);

                            long completed = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(dayStart) || recordDateTime.isEqual(dayStart)) &&
                                           recordDateTime.isBefore(dayEnd) &&
                                           "100".equals(record.getState());
                                })
                                .count();

                            long abnormal = records.stream()
                                .filter(record -> {
                                    long recordTime = record.getReceiveTime();
                                    LocalDateTime recordDateTime = LocalDateTime.ofEpochSecond(recordTime / 1000, 0, java.time.ZoneOffset.UTC);
                                    return (recordDateTime.isAfter(dayStart) || recordDateTime.isEqual(dayStart)) &&
                                           recordDateTime.isBefore(dayEnd) &&
                                           record.getState() != null && !"100".equals(record.getState());
                                })
                                .count();

                            experimentTrend.add(new ExperimentTrend(date, (int) completed, (int) abnormal));
                        }
                        break;
                }

                return experimentTrend;
            });
    }
}
